import { ISchema, Schema } from '@formily/json-schema'
import { ITreeNode } from '@designable/core'
import { clone, uid } from '@designable/shared'

/**
 * 转换器选项接口
 * 可指定设计器中的字段组件名和表单组件名
 */
export interface ITransformerOptions {
  designableFieldName?: string // 字段节点名
  designableFormName?: string // 表单节点名
}

/**
 * Formily Schema 转换结果接口
 * 包含 schema 和 form 配置
 */
export interface IFormilySchema {
  schema?: ISchema
  page?: Record<string, any>
}

/**
 * 合并默认选项和用户自定义选项
 * @param options 用户自定义选项
 * @returns 合并后的选项
 */
const createOptions = (options: ITransformerOptions): ITransformerOptions => {
  return {
    designableFieldName: 'Field', // 默认字段节点名
    designableFormName: 'Page', // 默认表单节点名
    ...options,
  }
}

/**
 * 在树结构中查找符合条件的节点
 * @param node 当前节点
 * @param finder 查找条件函数
 * @returns 找到的节点或 undefined
 */
const findNode = (node: ITreeNode, finder?: (node: ITreeNode) => boolean) => {
  if (!node) return
  if (finder(node)) return node
  if (!node.children) return
  for (let i = 0; i < node.children.length; i++) {
    // 递归查找
    if (findNode(node.children[i], finder)) return node.children[i]
  }
  return
}

/**
 * 将设计器的树节点结构转换为 Formily 的 schema 结构
 * @param node 设计器树节点
 * @param options 转换选项
 * @returns Formily schema 及表单属性
 */
export const transformToSchema = (
  node: ITreeNode,
  options?: ITransformerOptions
): IFormilySchema => {
  const realOptions = createOptions(options)
  // 查找根表单节点
  const root = findNode(node, (child) => {
    return child.componentName === realOptions.designableFormName
  })
  // 初始化 schema
  const schema = {
    type: 'object',
    properties: {},
  }
  if (!root) return { schema }
  /**
   * 递归生成 schema
   * @param node 当前节点
   * @param schema 当前 schema
   */
  const createSchema = (node: ITreeNode, schema: ISchema = {}) => {
    if (node !== root) {
      // 非根节点，合并节点属性到 schema
      Object.assign(schema, clone(node.props))
    }
    // 记录设计器节点 id
    schema['x-designable-id'] = node.id
    if (schema.type === 'array') {
      // 数组类型节点处理
      if (node.children[0]) {
        if (
          node.children[0].componentName === realOptions.designableFieldName
        ) {
          // 第一个子节点作为 items
          schema.items = createSchema(node.children[0])
          schema['x-index'] = 0
        }
      }
      // 处理剩余子节点，作为 properties
      node.children.slice(1).forEach((child, index) => {
        // if (child.componentName !== realOptions.designableFieldName) return
        const key = child.props.name || child.id
        schema.properties = schema.properties || {}
        schema.properties[key] = createSchema(child)
        schema.properties[key]['x-index'] = index
      })
    } else {
      // 普通对象类型节点处理
      node.children.forEach((child, index) => {
        // if (child.componentName !== realOptions.designableFieldName) return
        const key = child.props.name || child.id
        schema.properties = schema.properties || {}
        schema.properties[key] = createSchema(child)
        schema.properties[key]['x-index'] = index
      })
    }
    return schema
  }
  // 返回转换结果，包含表单属性和 schema
  return { page: clone(root.props), schema: createSchema(root, schema) }
}

/**
 * 将 Formily 的 schema 结构转换为设计器的树节点结构
 * @param formily Formily schema 及表单属性
 * @param options 转换选项
 * @returns 设计器树节点
 */
export const transformToTreeNode = (
  formily: IFormilySchema = {},
  options?: ITransformerOptions
) => {
  const realOptions = createOptions(options)
  // 构建根节点
  const root: ITreeNode = {
    componentName: realOptions.designableFormName,
    props: formily.page,
    children: [],
  }
  // 创建 Formily schema 实例
  const schema = new Schema(formily.schema)
  /**
   * 清理 schema 属性，去除无用字段
   * @param props 属性对象
   * @returns 清理后的属性
   */
  const cleanProps = (props: any) => {
    if (props['name'] === props['x-designable-id']) {
      delete props.name
    }
    delete props['version']
    delete props['_isJSONSchemaObject']
    return props
  }
  /**
   * 递归将 schema 转换为树节点
   * @param parent 父节点
   * @param schema 当前 schema
   */
  const appendTreeNode = (parent: ITreeNode, schema: Schema) => {
    if (!schema) return
    // 构建当前节点
    const current = {
      id: schema['x-designable-id'] || uid(),
      componentName: realOptions.designableFieldName,
      props: cleanProps(schema.toJSON(false)),
      children: [],
    }
    parent.children.push(current)
    // 处理数组类型的 items
    if (schema.items && !Array.isArray(schema.items)) {
      appendTreeNode(current, schema.items)
    }
    // 递归处理 properties
    schema.mapProperties((schema) => {
      schema['x-designable-id'] = schema['x-designable-id'] || uid()
      appendTreeNode(current, schema)
    })
  }
  // 根节点递归处理所有属性
  schema.mapProperties((schema) => {
    schema['x-designable-id'] = schema['x-designable-id'] || uid()
    appendTreeNode(root, schema)
  })
  return root
}
